using System;

using System.Data;
using System.Data.SqlClient;

using System.Diagnostics;

using System.Configuration;
using System.Configuration.Provider;

using System.Collections.Specialized;

using System.Web;
using System.Web.Security;
//using System.Web.Configuration;
using System.Web.SessionState;

using DotNetOpenId.RelyingParty;
using DotNetOpenId.Extensions.SimpleRegistration;
using DotNetOpenId;
using System.Collections.Generic;


namespace StrongEye.Identity.Consumer.OpenID
{
    public class OpenIDMembershipProvider : MembershipProvider
    {
        #region Attributes

        /// <summary>
        /// Event log Attribute. Source Name for the event.
        /// </summary>
        private string eventSource = "OpenIDMembershipProvider";

        /// <summary>
        /// Event log Attribute.
        /// </summary>
        private string eventLog = "Application";

        /// <summary>
        /// Event log Attribute. 
        /// </summary>
        private string exceptionMessage = "An exception occurred. Please check the Event Log.";

        /// <summary>
        /// Connection String for the Membership Provider.
        /// </summary>
        private string connectionString;

        /// <summary>
        /// Textbox Name where the user enter his OpenID Username.
        /// </summary>
        private string validateUserTextbox;

        /// <summary>
        /// Used when determining encryption key values.
        /// </summary>
        //private MachineKeySection machineKey;

        /// <summary>
        /// If true exceptions are written to the event log.
        /// </summary>
        private bool pWriteExceptionsToEventLog;

        /// <summary>
        /// System.Web.Security.MembershipProvider properties.
        /// </summary>
        private string pApplicationName;

        /// <summary>
        /// Optional Information taken from the configuration to retrieve from
        /// the OpenID  when the user login.
        /// </summary>
        private ClaimsRequest OptionalInformation = new ClaimsRequest();
        private ClaimsResponse OptionalInformationProvided = new ClaimsResponse();

        /// <summary>
        /// If True when the user is redirected from the openid provider to the application validates that the user exists 
        /// on the Membership Provider Database.
        /// </summary>
        private bool pCheckUserInDatabase;

        /// <summary>
        /// URL of the page that calls the Validate User Method of application.
        /// </summary>
        private string pLoginURL;

        private int pMinutesSinceLastActivity;

        #endregion

        #region Properties

        /// <summary>
        /// If True when the user is redirected from the openid provider to the application validates that the user exists 
        /// on the Membership Provider Database.
        /// </summary>
        public bool CheckUserInDatabase
        {
            get { return pCheckUserInDatabase; }
            set { pCheckUserInDatabase = value; }
        }

        public int MinutesSinceLastActivity
        {
            get { return pMinutesSinceLastActivity; }
        }

        /// <summary>
        /// URL of the page that calls the Validate User Method of application.
        /// </summary>
        public string LoginURL
        {
            get { return pLoginURL; }
        }

        /// <summary>
        /// Optional Information. Nickname asked to the OpenID Provider when the user login.
        /// </summary>
        public string Nickname
        {
            get { return OptionalInformationProvided.Nickname ?? ""; }
        }

        /// <summary>
        /// Optional Information. Email asked to the OpenID Provider when the user login.
        /// </summary>
        public string Email
        {
            get { return OptionalInformationProvided.Email ?? ""; }
        }

        /// <summary>
        /// Optional Information. Fullname asked to the OpenID Provider when the user login.
        /// </summary>
        public string Fullname
        {
            get { return OptionalInformationProvided.FullName ?? ""; }
        }

        /// <summary>
        /// Optional Information. Date of Birth asked to the OpenID Provider when the user login.
        /// </summary>
        public string Dob
        {
            get { return OptionalInformationProvided.BirthDate.HasValue ? OptionalInformationProvided.BirthDate.Value.ToString() : ""; }
        }

        /// <summary>
        /// Optional Information. Gender asked to the OpenID Provider when the user login.
        /// </summary>
        public string Gender
        {
            get { return OptionalInformationProvided.Gender.HasValue ? (OptionalInformationProvided.Gender.Value == DotNetOpenId.Extensions.SimpleRegistration.Gender.Male ? "M" : "F") : ""; }
        }

        /// <summary>
        /// Optional Information. Postcode asked to the OpenID Provider when the user login.
        /// </summary>
        public string Postcode
        {
            get { return OptionalInformationProvided.PostalCode ?? ""; }
        }

        /// <summary>
        /// Optional Information. Country asked to the OpenID Provider when the user login.
        /// </summary>
        public string Country
        {
            get { return OptionalInformationProvided.Country ?? ""; }
        }

        /// <summary>
        /// Optional Information. Language asked to the OpenID Provider when the user login.
        /// </summary>
        public string Language
        {
            get { return OptionalInformationProvided.Language ?? ""; }
        }

        /// <summary>
        /// Optional Information. Timezone asked to the OpenID Provider when the user login.
        /// </summary>
        public string Timezone
        {
            get { return OptionalInformationProvided.TimeZone ?? ""; }
        }

        /// <summary>
        /// Current Application Name
        /// </summary>
        public override string ApplicationName
        {
            get { return pApplicationName; }
            set { pApplicationName = value; }
        }

        /// <summary>
        /// Write Exceptions To EventLog
        /// </summary>
        public bool WriteExceptionsToEventLog
        {
            get { return pWriteExceptionsToEventLog; }
            set { pWriteExceptionsToEventLog = value; }
        }

        #endregion

        #region Initialize

        /// <summary>
        /// System.Configuration.Provider.ProviderBase.Initialize Method
        /// Initialize the Membership Provider Class
        /// </summary>
        public override void Initialize(string name, NameValueCollection config)
        {

            try
            {
                // Initialize values from web.config.
                if (config == null)
                    throw new ArgumentNullException("config");

                if (name == null || name.Length == 0)
                    name = "OpenIDMembershipProvider";

                if (String.IsNullOrEmpty(config["description"]))
                {
                    config.Remove("description");
                    config.Add("description", "OpenID Membership Provider");
                }

                // Initialize the abstract base class.
                base.Initialize(name, config);

                pApplicationName = GetConfigValue(config["applicationName"], System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath);
                pWriteExceptionsToEventLog = Convert.ToBoolean(GetConfigValue(config["writeExceptionsToEventLog"], "false"));

                // Initialize the OpenID Attributes
                validateUserTextbox = GetConfigValue(config["validateUserTextbox"], "Username");
                SetProfileRequestFromList(OptionalInformation, GetConfigValue(config["OptionalInformation"], "").Split(','), DemandLevel.Request);
                pMinutesSinceLastActivity = int.Parse(GetConfigValue(config["MinutesSinceLastActivity"], "20"));
                pCheckUserInDatabase = true;

                // Initialize the SqlConnection.
                ConnectionStringSettings ConnectionStringSettings =
                  ConfigurationManager.ConnectionStrings[config["connectionStringName"]];

                if (ConnectionStringSettings == null || ConnectionStringSettings.ConnectionString.Trim() == "")
                {
                    throw new ProviderException("Connection string cannot be blank.");
                }

                connectionString = ConnectionStringSettings.ConnectionString;


                //// Get encryption and decryption key information from the configuration.
                //Configuration cfg =
                //  WebConfigurationManager.OpenWebConfiguration(System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath);
                //machineKey = (MachineKeySection)cfg.GetSection("system.web/machineKey");
            }
            catch (Exception e)
            {
                if (this.WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "Initialize");
                }

                throw new OpenIDMembershipProviderException(e.Message, e.Source, e.StackTrace);
            }
        }

        /// <summary>
        /// A helper function to retrieve config values from the configuration file.
        /// </summary>
        /// <param name="configValue">The value taken from the configuration file</param>
        /// <param name="defaultValue">The default value is case that "configValue" is empty</param>
        /// <returns></returns>
        private string GetConfigValue(string configValue, string defaultValue)
        {
            if (String.IsNullOrEmpty(configValue))
                return defaultValue;

            return configValue;
        }

        /// <summary>
        /// A helper function to take a comma-delimited list of simple registration field names
        /// and convert them into the type-safe format exposed by the DotNetOpenId library.
        /// </summary>
        private void SetProfileRequestFromList(ClaimsRequest sregRequest, ICollection<string> fieldNames, DemandLevel requestLevel) {
            foreach (string field in fieldNames) {
                if (field == "") continue;
                switch (field) {
                    case "nickname":
                        sregRequest.Nickname = requestLevel;
                        break;
                    case "email":
                        sregRequest.Email = requestLevel;
                        break;
                    case "fullname":
                        sregRequest.FullName = requestLevel;
                        break;
                    case "dob":
                        sregRequest.BirthDate = requestLevel;
                        break;
                    case "gender":
                        sregRequest.Gender = requestLevel;
                        break;
                    case "postcode":
                        sregRequest.PostalCode = requestLevel;
                        break;
                    case "country":
                        sregRequest.Country = requestLevel;
                        break;
                    case "language":
                        sregRequest.Language = requestLevel;
                        break;
                    case "timezone":
                        sregRequest.TimeZone = requestLevel;
                        break;
                    default:
                        Trace.TraceWarning("Unrecognized field name '{0}'.", field);
                        break;
                }
            }
        }

        #endregion

        #region Create User

        /// <summary>
        /// MembershipProvider.CreateUser
        /// Creates a new User.
        /// </summary>
        /// <param name="username">OpenID Username</param>
        /// <param name="password"></param>
        /// <param name="email"></param>
        /// <param name="passwordQuestion"></param>
        /// <param name="passwordAnswer"></param>
        /// <param name="isApproved"></param>
        /// <param name="providerUserKey">the GUID, if it's value is null a new GUID is generated</param>
        /// <param name="status">Returns the type of error if it fail, or "Success" if the creation succeed </param>
        /// <returns>The Created User.</returns>
        public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer,
                                                    bool isApproved, object providerUserKey, out MembershipCreateStatus status)
        {
            // Normalize the username before adding it to the database.
            // We do this whole construction, create, and compare in the Debug.Assert call
            // so that we're not needlessly slowing down Release builds.
            Debug.Assert(username == new OpenIdRelyingParty().CreateRequest(username).ClaimedIdentifier,
                "Username has not been normalized.  Inconsistencies in database may result.");

            // Search the user in the Membership Provider Database to make sure the user doesn't exist alredy.
            MembershipUser u = GetUser(username, false);

            // If the user does not exists
            if (u == null)
            {

                DateTime createDate = DateTime.Now;

                // Generate the new GUID
                if (providerUserKey == null)
                {
                    providerUserKey = Guid.NewGuid();
                }
                else
                {
                    // If the User supply the GUID, it is verified
                    if (!(providerUserKey is Guid))
                    {
                        // Return Invalid Provider User Key
                        status = MembershipCreateStatus.InvalidProviderUserKey;
                        return null;
                    }
                }

                // Prepare the connection to the Database.
                SqlConnection conn = new SqlConnection(connectionString);
                SqlCommand cmd = new SqlCommand("aspnet_Membership_CreateUser", conn);
                cmd.CommandType = CommandType.StoredProcedure;

                cmd.Parameters.Add("@ApplicationName", SqlDbType.VarChar, 255).Value = this.ApplicationName;
                cmd.Parameters.Add("@Username", SqlDbType.VarChar, 255).Value = username;
                cmd.Parameters.Add("@Password", SqlDbType.VarChar, 128).Value = GenerateNewGUID();
                cmd.Parameters.Add("@PasswordSalt", SqlDbType.VarChar, 255).Value = GenerateNewSalt();
                cmd.Parameters.Add("@Email", SqlDbType.VarChar, 255).Value = "";
                cmd.Parameters.Add("@PasswordQuestion", SqlDbType.VarChar, 255).Value = GenerateNewGUID();
                cmd.Parameters.Add("@PasswordAnswer", SqlDbType.VarChar, 255).Value = GenerateNewGUID();
                cmd.Parameters.Add("@PasswordFormat", SqlDbType.Int).Value = 1;
                cmd.Parameters.Add("@IsApproved", SqlDbType.Bit).Value = isApproved;
                cmd.Parameters.Add("@CurrentTimeUtc", SqlDbType.DateTime).Value = DateTime.Now;
                cmd.Parameters.Add("@UserId", SqlDbType.UniqueIdentifier).Value = providerUserKey;

                try
                {
                    // Execute the Store Procedure
                    conn.Open();
                    int recAdded = cmd.ExecuteNonQuery();
                    // If the row was added
                    if (recAdded > 0)
                    {
                        // Set the status in Success
                        status = MembershipCreateStatus.Success;
                    }
                    // If the row wasn't added set status in User Reject
                    else
                    {
                        status = MembershipCreateStatus.UserRejected;
                    }
                }
                catch (SqlException e)
                {
                    if (WriteExceptionsToEventLog)
                    {
                        WriteToEventLog(e, "CreateUser");
                    }

                    status = MembershipCreateStatus.ProviderError;
                    throw new OpenIDMembershipProviderException(e.Message, e.Source, e.StackTrace);
                }
                finally
                {
                    conn.Close();
                }

                // Return the User just created
                return GetUser(username, false);
            }
            // If the user alredy exists on the database
            else
            {
                status = MembershipCreateStatus.DuplicateUserName;
            }

            return null;
        }

        private string GenerateNewSalt()
        {
            byte[] saltInBytes = new byte[16];
            System.Security.Cryptography.RNGCryptoServiceProvider saltGenerator = new System.Security.Cryptography.RNGCryptoServiceProvider();
            saltGenerator.GetBytes(saltInBytes);
            return Convert.ToBase64String(saltInBytes);
        }

        private string GenerateNewGUID()
        {
            return Guid.NewGuid().ToString();
        }

        #endregion

        #region Delete User

        /// <summary>
        /// MembershipProvider.DeleteUser
        /// Deletes the User from the Membership Provider
        /// </summary>
        /// <param name="username">The desire user to delete</param>
        /// <param name="deleteAllRelatedData">If deleteAllRelatedData is true, user data stored in the database for the Roles, Profile, or WebPart personalization is also deleted when you are using the SqlRoleProvider, SqlProfileProvider, and SqlPersonalizationProvider objects for data storage.</param>
        /// <returns>True if the delete was succeed</returns>
        public override bool DeleteUser(string username, bool deleteAllRelatedData)
        {
            // Prepare the connection to the Database.
            SqlConnection conn = new SqlConnection(connectionString);
            SqlCommand cmd = new SqlCommand("aspnet_Users_DeleteUser", conn);
            cmd.CommandType = CommandType.StoredProcedure;

            // The number of tables affected
            int tablesAffected = 0;

            cmd.Parameters.Add("@ApplicationName", SqlDbType.VarChar, 255).Value = this.ApplicationName;
            cmd.Parameters.Add("@Username", SqlDbType.VarChar, 255).Value = username;
            cmd.Parameters.Add("@TablesToDeleteFrom", SqlDbType.Int).Value = deleteAllRelatedData;
            cmd.Parameters.Add("@NumTablesDeletedFrom", SqlDbType.Int).Value = tablesAffected;

            // The number of rows affected
            int rowsAffected = 0;

            try
            {
                // Execute the Store Procedure
                conn.Open();
                rowsAffected = cmd.ExecuteNonQuery();
            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "DeleteUser");
                }

                throw new OpenIDMembershipProviderException(e.Message, e.Source, e.StackTrace);
            }
            finally
            {
                conn.Close();
            }

            if (rowsAffected > 0)
                return true;

            return false;
        }
        #endregion

        #region Get Users

        /// <summary>
        /// MembershipProvider.GetAllUsers
        /// </summary>
        /// <param name="pageIndex">Page Index</param>
        /// <param name="pageSize">Page Size</param>
        /// <param name="totalRecords">Total of records returned</param>
        /// <returns>A collection with all the Users from the Membership Provider filtered by Page Index and Page Size</returns>
        public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
        {
            // Prepare the connection to the Database.
            SqlConnection conn = new SqlConnection(connectionString);
            SqlCommand cmd = new SqlCommand("aspnet_Membership_GetAllUsers", conn);
            cmd.CommandType = CommandType.StoredProcedure;

            cmd.Parameters.Add("@ApplicationName", SqlDbType.VarChar, 255).Value = this.ApplicationName;
            cmd.Parameters.Add("@PageIndex", SqlDbType.Int).Value = pageIndex;
            cmd.Parameters.Add("@PageSize", SqlDbType.Int).Value = pageSize;

            // The collections of Users
            MembershipUserCollection users = new MembershipUserCollection();
            SqlDataReader reader = null;

            // Total of records found
            totalRecords = 0;

            try
            {
                // Execute the Store Procedure
                conn.Open();
                reader = cmd.ExecuteReader();

                while (reader.Read())
                {
                    //Add the users to the collection
                    MembershipUser u = GetUserFromReader(reader);
                    users.Add(u);
                    totalRecords++;
                }
            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "GetAllUsers ");
                }
                throw new OpenIDMembershipProviderException(e.Message, e.Source, e.StackTrace);
            }
            finally
            {
                if (reader != null) { reader.Close(); }
                conn.Close();
            }

            // Return the user collection
            return users;
        }


        /// <summary>
        /// MembershipProvider.GetAllUsers
        /// </summary>
        /// <param name="totalRecords">Total of records returned</param>
        /// <returns>A collection with all the users found in the Membership Provider</returns>
        public MembershipUserCollection GetAllUsers(out int totalRecords)
        {
            // Prepare the connection to the database
            SqlConnection conn = new SqlConnection(connectionString);
            SqlCommand cmd = new SqlCommand("StrongEye_OpenID_Membership_GetAllUsers", conn);
            cmd.CommandType = CommandType.StoredProcedure;

            cmd.Parameters.Add("@ApplicationName", SqlDbType.VarChar, 255).Value = this.ApplicationName;

            // The collection of Users
            MembershipUserCollection users = new MembershipUserCollection();
            SqlDataReader reader = null;

            // Total of records return
            totalRecords = 0;

            try
            {
                // Execute the Store Procedure
                conn.Open();
                reader = cmd.ExecuteReader();

                while (reader.Read())
                {
                    // Add the user to the collection
                    MembershipUser u = GetUserFromReader(reader);
                    users.Add(u);
                    totalRecords++;
                }
            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "GetAllUsers ");

                    throw new ProviderException(exceptionMessage);
                }

                throw new OpenIDMembershipProviderException(e.Message, e.Source, e.StackTrace);
            }
            finally
            {
                if (reader != null) { reader.Close(); }
                conn.Close();
            }

            // Return the collection users
            return users;
        }

        #endregion

        #region Get User

        /// <summary>
        /// MembershipProvider.GetUser(string, bool)
        /// </summary>
        /// <param name="username">Username of the desired User</param>
        /// <param name="userIsOnline">If true modify the lastlogindate of the user</param>
        /// <returns>The User Information related to the username</returns>
        public override MembershipUser GetUser(string username, bool userIsOnline)
        {
            // Prepare the connection to the Database
            SqlConnection conn = new SqlConnection(connectionString);
            SqlCommand cmd = new SqlCommand("StrongEye_OpenID_Membership_GetUserByName", conn);
            cmd.CommandType = CommandType.StoredProcedure;

            cmd.Parameters.Add("@ApplicationName", SqlDbType.VarChar, 255).Value = this.ApplicationName;
            cmd.Parameters.Add("@UserName", SqlDbType.VarChar, 255).Value = username;
            cmd.Parameters.Add("@CurrentTimeUtc", SqlDbType.DateTime).Value = DateTime.Now;
            cmd.Parameters.Add("@UpdateLastActivity", SqlDbType.Bit).Value = userIsOnline;

            MembershipUser u = null;
            SqlDataReader reader = null;

            try
            {
                // Execute the Store Procedure
                conn.Open();
                reader = cmd.ExecuteReader();
                if (reader.HasRows)
                {
                    reader.Read();
                    u = GetUserFromReader(reader);
                }
            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "GetUser(String, Boolean)");
                }
                throw new OpenIDMembershipProviderException(e.Message, e.Source, e.StackTrace);
            }
            finally
            {
                if (reader != null) { reader.Close(); }

                conn.Close();
            }

            // Return the User Information
            return u;
        }

        /// <summary>
        /// MembershipProvider.GetUser(object, bool)
        /// </summary>
        /// <param name="providerUserKey">The GUID of the desired User</param>
        /// <param name="userIsOnline">If true modify the lastlogindate of the user</param>
        /// <returns>The User Information related to the username</returns>
        public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
        {

            // Prepare the connection to the database
            SqlConnection conn = new SqlConnection(connectionString);
            SqlCommand cmd = new SqlCommand("StrongEye_OpenID_Membership_GetUserByUserId", conn);
            cmd.CommandType = CommandType.StoredProcedure;

            cmd.Parameters.Add("@UserId", SqlDbType.UniqueIdentifier).Value = providerUserKey;
            cmd.Parameters.Add("@CurrentTimeUtc", SqlDbType.DateTime).Value = DateTime.Now;
            cmd.Parameters.Add("@UpdateLastActivity", SqlDbType.Bit).Value = userIsOnline;


            MembershipUser u = null;
            SqlDataReader reader = null;

            try
            {
                // Execute the Store Procedure
                conn.Open();
                reader = cmd.ExecuteReader();

                if (reader.HasRows)
                {
                    reader.Read();
                    u = GetUserFromReader(reader);
                }
            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "GetUser(Object, Boolean)");
                }

                throw new OpenIDMembershipProviderException(e.Message, e.Source, e.StackTrace);
            }
            finally
            {
                if (reader != null) { reader.Close(); }
                conn.Close();
            }

            // Return the User Information
            return u;
        }

        #endregion

        #region Get Users Online

        /// <summary>
        /// MembershipProvider.GetNumberOfUsersOnline
        /// </summary>
        /// <returns>Return the numbers of users online, according to the "MinutesSinceLastActivity" attribute</returns>
        public override int GetNumberOfUsersOnline()
        {
            // Prepare the connection to the database
            SqlConnection conn = new SqlConnection(connectionString);
            SqlCommand cmd = new SqlCommand("aspnet_Membership_GetNumberOfUsersOnline", conn);
            cmd.CommandType = CommandType.StoredProcedure;

            cmd.Parameters.Add("@ApplicationName", SqlDbType.VarChar, 255).Value = this.ApplicationName;
            cmd.Parameters.Add("@MinutesSinceLastInActive", SqlDbType.VarChar, 255).Value = MinutesSinceLastActivity;
            cmd.Parameters.Add("@CurrentTimeUtc", SqlDbType.DateTime).Value = DateTime.Now;

            // Number of users online
            int NumberOfUsersOnline = 0;
            SqlDataReader reader = null;

            try
            {
                // Execute the Store Procedure
                conn.Open();
                reader = cmd.ExecuteReader();
                if (reader.Read())
                {
                    NumberOfUsersOnline = int.Parse(reader[0].ToString());
                }
            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "GetNumberOfUsersOnline()");
                }
                throw new OpenIDMembershipProviderException(e.Message, e.Source, e.StackTrace);
            }
            finally
            {
                if (reader != null) { reader.Close(); }
                conn.Close();
            }

            return NumberOfUsersOnline;
        }

        #endregion

        #region Update User

        /// <summary>
        /// MembershipProvider.UpdateUser
        /// </summary>
        /// <param name="user">User Information to update</param>
        public override void UpdateUser(MembershipUser user)
        {
            // Prepare the connection to the database
            SqlConnection conn = new SqlConnection(connectionString);
            SqlCommand cmd = new SqlCommand("aspnet_Membership_UpdateUser", conn);
            cmd.CommandType = CommandType.StoredProcedure;

            cmd.Parameters.Add("@ApplicationName", SqlDbType.VarChar, 255).Value = this.ApplicationName;
            cmd.Parameters.Add("@Username", SqlDbType.VarChar, 255).Value = user.UserName;
            cmd.Parameters.Add("@Email", SqlDbType.VarChar, 255).Value = user.Email;
            cmd.Parameters.Add("@Comment", SqlDbType.VarChar, 255).Value = user.Comment;
            cmd.Parameters.Add("@IsApproved", SqlDbType.Bit).Value = user.IsApproved;
            cmd.Parameters.Add("@LastLoginDate", SqlDbType.DateTime).Value = user.LastLoginDate;
            cmd.Parameters.Add("@LastActivityDate", SqlDbType.DateTime).Value = user.LastActivityDate;
            cmd.Parameters.Add("@UniqueEmail", SqlDbType.Int).Value = false;
            cmd.Parameters.Add("@CurrentTimeUtc", SqlDbType.DateTime).Value = DateTime.Now;

            try
            {
                // Execute the Store Procedure
                conn.Open();
                cmd.ExecuteNonQuery();
            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "UpdateUser");
                }
                throw new OpenIDMembershipProviderException(e.Message, e.Source, e.StackTrace);
            }
            finally
            {
                conn.Close();
            }
        }

        #endregion

        #region Validate User

        /// <summary>
        /// MembershipProvider.ValidateUser
        /// </summary>
        /// <param name="username">Username</param>
        /// <param name="password">Password is ignored</param>
        /// <returns>False if the user enter invalid information, otherwise the User is redirect to the OpenID provider.</returns>
        public override bool ValidateUser(string username, string password)
        {
            bool ret = true;
            HttpContext Context = HttpContext.Current;
            HttpSessionState Session = Context.Session;
            HttpRequest Request = Context.Request;
            HttpResponse Response = Context.Response;
            OpenIdRelyingParty consumer = new OpenIdRelyingParty();

            if (username != null)
            {
                // Get the current page
                pLoginURL = Context.Request.Url.AbsoluteUri; 
                try
                {
                    // Initiate openid request
                    IAuthenticationRequest request = consumer.CreateRequest(username);
                    request.AddCallbackArguments("ReturnUrl", Request.QueryString["ReturnUrl"]);
                    // The following illustrates how to use SREG. 
                    request.AddExtension(OptionalInformation);
                    // Redirect the user to the OpenID provider Page
                    request.RedirectToProvider(true);
                }
                catch (OpenIdException fexc)
                {
                    if (WriteExceptionsToEventLog)
                    {
                        WriteToEventLog(fexc, "ValidateOpenIDUser");
                    }
                    throw new OpenIDMembershipProviderException(fexc.Message, fexc.Source, fexc.StackTrace);
                }
                catch (System.Threading.ThreadAbortException)
                {
                    // Consume. This is normal during redirect.
                }
                catch (Exception fe)
                {
                    if (WriteExceptionsToEventLog)
                    {
                        WriteToEventLog(fe, "ValidateOpenIDUser");
                    }
                    throw new OpenIDMembershipProviderException(fe.Message, fe.Source, fe.StackTrace);
                }
            }
            else
            {
                ret = false;
            }
            return ret;
        }

        private String IdentityUrlToDisplayString(Identifier identityUrl)
        {
            String absoluteUri = identityUrl;
            if (absoluteUri.StartsWith("xri:"))
            {
                absoluteUri = absoluteUri.Substring("xri:".Length);
            }
            return absoluteUri;
        }

        /// <summary>
        /// MembershipProvider.ValidateUser
        /// Second part of the User Validation
        /// </summary>
        /// <returns>True if the user was successfully validated from the OpenID Provider </returns>
        public bool ValidateOpenIDUser()
        {
            bool ret = true;
            HttpContext Context = HttpContext.Current;
            HttpSessionState Session = Context.Session;
            HttpRequest Request = Context.Request;
            OpenIdRelyingParty consumer = new OpenIdRelyingParty();
            IAuthenticationResponse resp = consumer.Response;

            // Checks that the user was successfully validated from the OpenID Provider
            if (resp != null && resp.Status == AuthenticationStatus.Authenticated)
            {
                try
                {
                    // Gets the Username
                    string userIdentity = IdentityUrlToDisplayString(resp.ClaimedIdentifier);

                    //Get the Optional Information
                    OptionalInformationProvided = resp.GetExtension<ClaimsResponse>() ?? new ClaimsResponse();

                    // If the option is set to True the user is also validated in the Membership Provider Database
                    if (pCheckUserInDatabase)
                    {
                        MembershipUser user = GetUser(userIdentity, true);
                        ret = (user != null);
                    }

                    // If the user is valid is authenticated with Forms Authentication
                    if (ret)
                    {
                        FormsAuthentication.RedirectFromLoginPage(userIdentity, false);
                    }

                }
                catch (OpenIdException fexc)
                {
                    if (WriteExceptionsToEventLog)
                    {
                        WriteToEventLog(fexc, "ValidateOpenIDUser");
                    }
                    ret = false;
                }
                catch (Exception fe)
                {
                    if (WriteExceptionsToEventLog)
                    {
                        WriteToEventLog(fe, "ValidateOpenIDUser");
                    }
                    throw new OpenIDMembershipProviderException(fe.Message, fe.Source, fe.StackTrace);
                }
            }
            else
            {
                ret = false;
            }
            return ret;
        }

        /// <summary>
        /// Converts a hexadecimal string to a byte array. Used to convert encryption
        /// key values from the configuration.
        /// </summary>
        /// <param name="hexString"></param>
        /// <returns></returns>
        private byte[] HexToByte(string hexString)
        {
            byte[] returnBytes = new byte[hexString.Length / 2];
            for (int i = 0; i < returnBytes.Length; i++)
                returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
            return returnBytes;
        }

        #endregion

        #region FindUsersByName

        public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
        {
            // Prepare the connection to the Database.
            SqlConnection conn = new SqlConnection(connectionString);
            SqlCommand cmd = new SqlCommand("aspnet_Membership_FindUsersByName", conn);
            cmd.CommandType = CommandType.StoredProcedure;

            cmd.Parameters.Add("@ApplicationName", SqlDbType.VarChar, 255).Value = this.ApplicationName;
            cmd.Parameters.Add("@UserNameToMatch", SqlDbType.VarChar, 255).Value = usernameToMatch;
            cmd.Parameters.Add("@PageIndex", SqlDbType.Int).Value = pageIndex;
            cmd.Parameters.Add("@PageSize", SqlDbType.Int).Value = pageSize;

            // The collections of Users
            MembershipUserCollection users = new MembershipUserCollection();
            SqlDataReader reader = null;

            // Total of records found
            totalRecords = 0;

            try
            {
                // Execute the Store Procedure
                conn.Open();
                reader = cmd.ExecuteReader();

                while (reader.Read())
                {
                    //Add the users to the collection
                    MembershipUser u = GetUserFromReader(reader);
                    users.Add(u);
                    totalRecords++;
                }
            }
            catch (SqlException e)
            {
                if (WriteExceptionsToEventLog)
                {
                    WriteToEventLog(e, "GetAllUsers ");
                }
                throw new OpenIDMembershipProviderException(e.Message, e.Source, e.StackTrace);
            }
            finally
            {
                if (reader != null) { reader.Close(); }
                conn.Close();
            }

            // Return the user collection
            return users;
        }

        #endregion

        #region Get User From Reader

        /// <summary>
        /// GetUserFromReader
        /// A helper function that takes the current row from the SqlDataReader
        /// and hydrates a MembershiUser from the values. Called by the 
        /// MembershipUser.GetUser and MembershipUser.GetAllUsers implementation.
        /// </summary>
        /// <param name="reader">DataReader with the User Information</param>
        /// <returns>A populated MembershipUser according the DataReader</returns>
        private MembershipUser GetUserFromReader(SqlDataReader reader)
        {
            string username = reader.GetString(0);
            string email = reader.GetString(1);
            string passwordQuestion = "";
            if (reader.GetValue(2) != DBNull.Value)
                passwordQuestion = reader.GetString(2);

            string comment = "";
            if (reader.GetValue(3) != DBNull.Value)
                comment = reader.GetString(3);

            bool isApproved = reader.GetBoolean(4);

            DateTime creationDate = reader.GetDateTime(5);

            DateTime lastLoginDate = new DateTime();
            if (reader.GetValue(6) != DBNull.Value)
                lastLoginDate = reader.GetDateTime(6);

            DateTime lastActivityDate = reader.GetDateTime(7);

            DateTime lastPasswordChangedDate = reader.GetDateTime(8);

            object providerUserKey = reader.GetValue(9);

            bool isLockedOut = reader.GetBoolean(10);

            DateTime lastLockedOutDate = new DateTime();
            if (reader.GetValue(11) != DBNull.Value)
                lastLockedOutDate = reader.GetDateTime(11);

            MembershipUser u = new MembershipUser(this.Name,
                                                  username,
                                                  providerUserKey,
                                                  email,
                                                  passwordQuestion,
                                                  comment,
                                                  isApproved,
                                                  isLockedOut,
                                                  creationDate,
                                                  lastLoginDate,
                                                  lastActivityDate,
                                                  lastPasswordChangedDate,
                                                  lastLockedOutDate);

            return u;
        }

        #endregion

        #region EventLog Methods

        /// <summary>
        /// WriteToEventLog
        /// A helper function that writes exception detail to the event log. Exceptions
        /// are written to the event log as a security measure to avoid private database
        /// details from being returned to the browser. If a method does not return a status
        /// or boolean indicating the action succeeded or failed, a generic exception is also 
        /// thrown by the caller.
        /// </summary>
        /// <param name="e">Expection</param>
        /// <param name="action"></param>
        private void WriteToEventLog(Exception e, string action)
        {
            EventLog log = new EventLog();
            log.Source = eventSource;
            log.Log = eventLog;

            string message = "An exception occurred communicating with the data source.\n\n";
            message += "Action: " + action + "\n\n";
            message += "Exception: " + e.ToString();

            log.WriteEntry(message);
        }

        #endregion

        #region Not Suported Methods

        /* **************************************************************************/
        /* *************** METHODS AND PROPERTIES NOT SUPPORTED *********************/
        /* **************************************************************************/

        public override bool EnablePasswordReset
        {
            get { return false; }
        }

        public override bool EnablePasswordRetrieval
        {
            get { return false; }
        }

        public override bool RequiresQuestionAndAnswer
        {
            get { return false; }
        }

        public override bool RequiresUniqueEmail
        {
            get { return false; }
        }

        public override int MaxInvalidPasswordAttempts
        {
            get { return 0; }
        }

        public override int PasswordAttemptWindow
        {
            get { return 0; }
        }

        public override MembershipPasswordFormat PasswordFormat
        {
            get { throw new NotSupportedException(); }
        }

        public override int MinRequiredNonAlphanumericCharacters
        {
            get { return 0; }
        }

        public override int MinRequiredPasswordLength
        {
            get { return 0; }
        }

        public override string PasswordStrengthRegularExpression
        {
            get { return ""; }
        }

        //
        // System.Web.Security.MembershipProvider methods.
        //

        //
        // MembershipProvider.ChangePassword
        //
        public override bool ChangePassword(string username, string oldPwd, string newPwd)
        {
            throw new NotSupportedException();
        }

        //
        // MembershipProvider.ChangePasswordQuestionAndAnswer
        //
        public override bool ChangePasswordQuestionAndAnswer(string username,
                      string password,
                      string newPwdQuestion,
                      string newPwdAnswer)
        {
            throw new NotSupportedException();
        }

        //
        // MembershipProvider.GetPassword
        //
        public override string GetPassword(string username, string answer)
        {
            throw new NotSupportedException();
        }

        //
        // MembershipProvider.UnlockUser
        //
        public override bool UnlockUser(string username)
        {
            throw new NotSupportedException();
        }

        //
        // MembershipProvider.GetUserNameByEmail
        //
        public override string GetUserNameByEmail(string email)
        {
            throw new NotSupportedException();
        }

        //
        // MembershipProvider.ResetPassword
        //
        public override string ResetPassword(string username, string answer)
        {
            throw new NotSupportedException();
        }

        //
        // UpdateFailureCount
        //   A helper method that performs the checks and updates associated with
        // password failure tracking.
        //
        private void UpdateFailureCount(string username, string failureType)
        {
            throw new NotSupportedException();
        }

        //
        // CheckPassword
        //   Compares password values based on the MembershipPasswordFormat.
        //
        private bool CheckPassword(string password, string dbpassword)
        {
            throw new NotSupportedException();
        }

        //
        // EncodePassword
        //   Encrypts, Hashes, or leaves the password clear based on the PasswordFormat.
        //
        private string EncodePassword(string password)
        {
            throw new NotSupportedException();
        }

        //
        // UnEncodePassword
        //   Decrypts or leaves the password clear based on the PasswordFormat.
        //
        private string UnEncodePassword(string encodedPassword)
        {
            throw new NotSupportedException();
        }

        public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
        {
            throw new NotSupportedException();
        }

        #endregion

    }
}